iT邦幫忙

0

[VS] C# - [02] EmguCV 影像處理(灰階、通道擷取、遮罩相減)

Nil 2022-09-21 16:53:532475 瀏覽
  • 分享至 

  • xImage
  •  

摘要

本章節將把讀取的影像進行處理如灰階、分別擷取R, G, B通道、針對影像通道減少顏色操作,最後把顯示的影像做保存,本篇依照上一篇[VS] C# - [01] EmguCV 影像讀取的程式繼續操作。


實作

介面設置

介面的部份把各轉換的方式分成不同的按鈕進行使用。


圖一 主程式介面

Enum

在轉換影像的時候,很多按鈕處理的方式大同小異,因此我們會把重複性高的程式做一個函數,使用該函數只要丟進影像與轉換類型即可獲得預期輸出,使用字串的方式傳遞會容易使錯誤,所以採用 enum (列舉)的方式建立我們所需要的類型。

public enum EColorType
{
    Blue = 0,
    Green,
    Red,
    Gray
}

灰階與影像通道處理

我們接取的影像型態為Image<Bgr, Byte>,經過轉換灰階或者是單獨擷取通道的時候皆為一個通道,因此在輸出的影像時候則為Image<Gray, Byte>的型態。

雖說接下來在按鈕按下的時候會先檢測影像是否存在,但為了安全起見,還是會從函式裡面做一個檢測影像是否為 null ,若為 null 透過 throw new 的方式建立我們所需要擲出的例外(Exception)資訊。

我們輸入的影像型態為 Bgr 的格式,因此我們使用 a_bitmap[index] 當中的 index 位置做為通道如 [0] = B, [1] = G, [2] = R,因此我們只要將通道擷取出來即可完成我們要提取的通道。

Bgr 的格式轉換成 Gray 的方式也只需要透過Convert<TColor, TDepth>方式進行轉換。

參考資料:Image<TColor, TDepth>.Convert Method

private Image<Gray, Byte> ChanneConvert1(Image<Bgr, Byte> a_bitmap, EColorType a_type)
{
    if (a_bitmap == null)
    {
        throw new NullReferenceException("影像不得為空。");
    }

    Image<Gray, Byte> convertImage;

    // 顏色轉換
    switch (a_type)
    {
        case EColorType.Blue:
            convertImage = a_bitmap[0].Clone();
            break;
        case EColorType.Green:
            convertImage = a_bitmap[1].Clone();
            break;
        case EColorType.Red:
            convertImage = a_bitmap[2].Clone();
            break;
        case EColorType.Gray:
            convertImage = a_bitmap.Convert<Gray, Byte>().Clone();
            break;
        default:
            throw new NullReferenceException("無其他類型。");
    }

    return convertImage;
}

若不想要影像進來時因為擲出例外使程式中斷,可以採用回傳空影像(預設影像)的方式並帶著錯誤的資訊顯示給使用者,下面的方法則是將影像直接做一個 new Image<Gray, Byte> 並在每個像素預設為 Gray(255) ,也就是預設白影像。

convertImage = new Image<Gray, Byte>(m_image.Width, m_image.Height, new Gray(255));
MessageBox.Show("影像顯示錯誤。", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);

保留顏色

這邊保留顏色指的是使用原圖pixelA=(233, 50, 30)遮罩(0, 255, 255)進行相減保留藍色的像素,最後會變成pixelA=(233, 0, 0),當然你也可以用圖片作為參考遮罩,也許該稱為濾鏡比較適當。

Image<TColor, TDepth>.Sub Method

private Image<Bgr, Byte> ChannelReduce1(Image<Bgr, Byte> a_bitmap, EColorType a_type)
{
    if (a_bitmap == null)
    {
        throw new NullReferenceException("影像不得為空。");
    }

    Image<Bgr, Byte> convertImage;

    // 顏色轉換
    switch (a_type)
    {
        case EColorType.Blue:
            convertImage = a_bitmap.Sub(new Bgr(0, 255, 255)).Clone();
            break;
        case EColorType.Green:
            convertImage = a_bitmap.Sub(new Bgr(255, 0, 255)).Clone();
            break;
        case EColorType.Red:
            convertImage = a_bitmap.Sub(new Bgr(255, 255, 0)).Clone();
            break;
        default:
            throw new NullReferenceException("無其他類型。");
    }

    return convertImage;
}

保存影像

Image<TColor, TDepth> 格式眾多,為了方便這邊直接使用 Bitmap 的方式進行影像保存,只要使用 Save() 的函式並帶上路徑與檔案名稱就可以完成保存,為了讓開發者了解影像是否保存成功,使用 bool 的方式來進行響應,因路徑或者是檔名可能會有錯誤,因此採用 try catch 的方式掌控。

private bool SaveImage(Bitmap a_image, string a_path, string a_fileName)
{
    try
    {
        a_image.Save($"{a_path}/{a_fileName}");
        return true;
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return false;
    }
}

元件

因大部分影像處理的功能皆為重複操作,因此只用按鈕第一個(藍)色作為顯示,只需要改變函式中的 EColorType.{名稱} 就可以對應各功能了。

灰階影像:

private void button2GrayImage_Click(object sender, EventArgs e)
{
    if (m_image == null) return;

    pictureBoxDisplayImage.Image = (Bitmap)ChanneConvert1(m_image, EColorType.Gray).ToBitmap().Clone();
    pictureBoxDisplayImage.Invalidate();
}

藍:
private void buttonChannelBlue_Click(object sender, EventArgs e)
{
    if (m_image == null) return;

    pictureBoxDisplayImage.Image = (Bitmap)ChanneConvert1(m_image, EColorType.Blue).ToBitmap().Clone();
    pictureBoxDisplayImage.Invalidate();
}

保留藍:
private void buttonKeepBlue_Click(object sender, EventArgs e)
{
    if (m_image == null) return;
    
    pictureBoxDisplayImage.Image = (Bitmap)ChannelReduce1(m_image, EColorType.Blue).ToBitmap().Clone();
    pictureBoxDisplayImage.Invalidate();
}

影像保存:
private void buttonSaveImage_Click(object sender, EventArgs e)
{
    // "./" 代表當前路徑
    // 後面名稱需附上副檔名
    if (SaveImage((Bitmap)pictureBoxDisplayImage.Image, "./", "save_image.bmp") == true)
    {
        MessageBox.Show("保存影像成功。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
    } 
    else
    {
        MessageBox.Show("保存影像失敗。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
}

結果


圖二 灰階影像


圖三 藍通道


圖四 透過遮罩顯示的保留藍


圖五 保存影像結果

結論

EmguCV在影像的操作是非常的完整,在這邊你已經學會了如何把讀取影像進來的影像做簡易的處理,如整張影像灰階、提取通道、透過Sub遮罩減掉通道這些操作,下一篇我們將會學到 Panel 與 Dock 進行元件編排,以及使用 EmguCV 所提供的 ImageBox 元件取代 PictureBox。

Github: Link.


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言